#include "doorbell_app.h"
#include "doorbell_button.h"
#include "doorbell_camera.h"
#include "doorbell_codec.h"
#include "doorbell_led.h"
#include "doorbell_mqtt.h"
#include "doorbell_wifi.h"
#include "doorbell_ota.h"
#include "doorbell_wsclient.h"
#include "doorbell_lcd.h"
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "semaphore.h"

#define TAG "doorbell_app"
#define DOORBELL_VERSION "1.0.0"

extern const uint8_t dingdong_pcm_start[] asm("_binary_output_raw_start");
extern const uint8_t dingdong_pcm_end[] asm("_binary_output_raw_end");
static bool is_streaming = false;
static bool is_lcd_on = false;
static camera_fb_t *fb = NULL;
static SemaphoreHandle_t captrue_sem = NULL;
static SemaphoreHandle_t release_sem = NULL;

static void doorbell_app_camera_lcd_task(void *arg)
{
    void *lcd_buf = heap_caps_malloc(LCD_WIDTH * LCD_HEIGHT * 2, MALLOC_CAP_SPIRAM);
    while (1)
    {
        if (!is_lcd_on && !is_streaming)
        {
            vTaskDelay(pdMS_TO_TICKS(10));
            continue;
        }

        fb = doorbell_camera_capture();
        xSemaphoreGive(captrue_sem);

        if (is_lcd_on)
        {
            doorbell_camera_fb2rgb565(fb, lcd_buf);
            doorbell_lcd_show_image(lcd_buf);
        }

        if (is_streaming)
        {
            xSemaphoreTake(release_sem, portMAX_DELAY);
        }

        doorbell_camera_release(fb);
    }
    free(lcd_buf);
    vTaskDelete(NULL);
}

static void doorbell_app_mic_cb(void *arg, const void *buf, int len)
{
    doorbell_codec_write(buf, len);
}

static void doorbell_app_image_task(void *arg)
{
    // 从摄像头采集数据发送到服务器
    while (is_streaming)
    {
        xSemaphoreTake(captrue_sem, portMAX_DELAY);
        doorbell_wsclient_send_image(fb->buf, fb->len);
        xSemaphoreGive(release_sem);
    }
    vTaskDelete(NULL);
}

static void doorbell_app_audio_task(void *arg)
{
    void *buf = malloc(2048);
    // 从mic读数据发送到服务器
    while (is_streaming)
    {
        doorbell_codec_read(buf, 2048);
        doorbell_wsclient_send_audio(buf, 2048);
    }
    free(buf);
    vTaskDelete(NULL);
}

static void doorbell_app_button_cb(void *button_handle, void *arg)
{
    // 发送mqtt信息
    doorbell_mqtt_publish("{\"status\":\"dingdong\"}", 0);
    // 叮咚
    doorbell_codec_write(dingdong_pcm_start, dingdong_pcm_end - dingdong_pcm_start);
}

static void doorbell_app_switch_cb(void *arg)
{
    ESP_LOGI(TAG, "switch cmd received");
    if (is_streaming)
    {
        // 关闭推流
        is_streaming = false;
        vTaskDelay(pdMS_TO_TICKS(1000));
        doorbell_wsclient_disconnect();
    }
    else
    {
        // 开启推流
        is_streaming = true;
        // 连接websocket
        doorbell_wsclient_connect();
        xTaskCreate(doorbell_app_image_task, "image_task", 4096, NULL, 5, NULL);
        xTaskCreate(doorbell_app_audio_task, "audio_task", 4096, NULL, 5, NULL);
    }
}

static void doorbell_app_lcd_on_off(void *button_handle, void *arg)
{
    if (is_lcd_on)
    {
        is_lcd_on = false;
        doorbell_lcd_off();
    }
    else
    {
        is_lcd_on = true;
        doorbell_lcd_on();
    }
}

static void doorbell_app_ota_cb(void *arg)
{
    doorbell_ota_run();
}

void doorbell_app_run(void)
{
    doorbell_led_init();
    doorbell_button_init();
    doorbell_camera_init();
    doorbell_codec_init();
    doorbell_lcd_init();
    doorbell_wifi_init();
    doorbell_mqtt_init();
    doorbell_wsclient_init();

    doorbell_wsclient_register_audio_callback(doorbell_app_mic_cb, NULL);

    doorbell_codec_open();
    doorbell_codec_set_volume(60);
    doorbell_codec_set_mic_gain(10);

    captrue_sem = xSemaphoreCreateBinary();
    release_sem = xSemaphoreCreateBinary();
    xTaskCreate(doorbell_app_camera_lcd_task, "lcd_task", 4096, NULL, 5, NULL);

    // 1. 按键叮咚
    doorbell_button_register_front_callback(BUTTON_SINGLE_CLICK, doorbell_app_button_cb, NULL);

    // 2. 背键开关LCD
    doorbell_button_register_back_callback(BUTTON_SINGLE_CLICK, doorbell_app_lcd_on_off, NULL);

    // 2. 处理switch回调
    mqtt_cmd_t cmd = {
        .callback = doorbell_app_switch_cb,
        .arg = NULL,
        .cmd = "switch",
    };
    doorbell_mqtt_register_cmd(&cmd);

    mqtt_cmd_t ota_cmd = {
        .callback = doorbell_app_ota_cb,
        .arg = NULL,
        .cmd = "ota",
    };
    doorbell_mqtt_register_cmd(&ota_cmd);

    doorbell_mqtt_publish("{\"current_version\":\"" DOORBELL_VERSION "\"}", 0);

    ESP_LOGI(TAG, "Current version: %s", DOORBELL_VERSION);
}